package diploma.matrika;

import java.io.File;
import java.io.FileNotFoundException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import diploma.matrika.data.Par;
import diploma.substitucija.Buffer;
import diploma.substitucija.Substitucija;
import diploma.tokenizer.Tokenizer;

public class MatrikaParov
{ 
	
	
	public static enum eTipRazdalje {evklidska, manhattan, maximum}
	
	public static final int MAX_PAROV = 31;
	
	boolean normaliziraj = true;
	
	public double frekvencaParov[][];
	double sestevek;
	public MatrikaParov()
	{
		frekvencaParov = new double[MAX_PAROV][MAX_PAROV];
	}
	
	public double sestejVrstico(int index)
	{
		double sestevek = 0;
		for(int s = 1; s < MAX_PAROV; s++ )
		{
			sestevek += frekvencaParov[index][s];
		}
		return sestevek;
	}
	
	public double sestejStolpec(int index)
	{
		double sestevek = 0;
		for(int v = 1; v < MAX_PAROV; v++ )
		{
			sestevek += frekvencaParov[v][index];
		}
		return sestevek;
	}
	
	private void resetMatrix()
	{
		sestevek = 0;
		for(int i = 0; i < MAX_PAROV; i++)
			for(int j = 0; j < MAX_PAROV; j++)
				frekvencaParov[i][j] = 0;
	}
	

	public void posodobiFrekvence(List<String> lstTokens)
	{
		resetMatrix();
		for(String beseda : lstTokens)
		{
			posodobiBesedo(beseda);
		}
		if(normaliziraj)
			normalizirajMatriko();
	}
	
	public void permutacija(int what, int with)
	{
		if(what == with)
			return;
		zamenjajStolpca(what, with);
		zamenjajVrstici(what, with);
	}
	
	private void zamenjajStolpca(int what, int with)
	{
		//what prekopiramo v *
		for(int i = 1; i < MAX_PAROV; i++)
		{
			frekvencaParov[i][0] = frekvencaParov[i][what];
			frekvencaParov[i][what] = frekvencaParov[i][with];
			frekvencaParov[i][with] = frekvencaParov[i][0];
			frekvencaParov[i][0] = 0;
		}
	}
	
	private void zamenjajVrstici(int what, int with)
	{
		for(int i = 1; i < MAX_PAROV; i++)
		{
			frekvencaParov[0][i] = frekvencaParov[what][i];
			frekvencaParov[what][i] = frekvencaParov[with][i];
			frekvencaParov[with][i] = frekvencaParov[0][i];
			frekvencaParov[0][i] = 0;
		}
	}
	
	private void normalizirajMatriko()
	{
		for(int i = 0; i < MAX_PAROV; i++)
			for(int j = 0; j < MAX_PAROV; j++)
				frekvencaParov[i][j] = frekvencaParov[i][j] / sestevek;
	}
	
	private void posodobiBesedo(String beseda)
	{
		for(int i = 0; i < beseda.length() - 1 ; i++)
		{
			CharSequence par = beseda.subSequence(i, i+2);
			posodobiPar(par);
		}
	}
	
	
	public static int getIndex(char c) 
	{ 
		int index = c - 'A';
		if(index >= 0 && index <= 25)
		{
			//Prišteje 1,  ker je index A-ja = 0. 
			return index+1;
		} else
		{
			//čšž
			switch (c)
			{
				case 'Č':
					return 27;
					
				case 'Š':
					return 28;
					
				case 'Ž':
					return 29;
					
				case ' ':
					return 30;
				
				default:
					//Te črke ni v naši abecedi		
					return 0;
			}
		}
	}
	
	//Inverzna funkcija metode int getIndex(char c)
	public static char getCharacter(int index) 
	{ 
		if(index >= 1 && index <= 26)
		{ 
			return (char)(index - 1 + 'A');
		} else
		{
			switch (index)
			{
				case  27:
					return 'Č';
					
				case  28:
					return 'Š';
					
				case  29:
					return 'Ž';
		
				case 30:
				return ' ';
				
				default:
					//Te črke ni v naši abecedi
					
					return '*';
			}
		}
	}
	
	private void posodobiPar(CharSequence par)
	{
		//dobi index vrstice za frekvencaParov (1. črka)
		int vrstica = getIndex(par.charAt(0));
		//dobi index stolpca za frekvencaParov (2. črka)
		int stolpec = getIndex(par.charAt(1));
		frekvencaParov[vrstica][stolpec]++;
		if(Math.min(vrstica, stolpec) != 0)
			sestevek++;
	}



	
	public void izpisiMatriko()
	{
		//Izpis natančen na 4 decimalke, dodaj # za izpis večje natančnosti
		DecimalFormat f = new DecimalFormat("#.#####");
		System.out.println("Ena je: " + f.format((1.0/sestevek)));
		//1. izpis oznake stolpcev
		System.out.print("\t");
		for(int i = 1; i < MAX_PAROV; i++)
		{
			System.out.print("   "+getCharacter(i) + "\t");
		}
		System.out.println();
		//2. izpišemo vsako vrstico posebej
		for(int v = 1; v < MAX_PAROV; v++) //v je index vrstice
		{
			//pred izpisom vrstice izpišemo oznako vrstice
			System.out.print(getCharacter(v)+ "\t");
			//izpis vrstice
			for(int s = 1; s < MAX_PAROV; s++) //s je index stolpca
			{
				System.out.print(f.format(frekvencaParov[v][s]) +"\t");
			}
			System.out.println();
		}
	}
	

	public List<Par> najdiMaxPare(int N)
	{
		List<Par> lstPari = new ArrayList<Par>();
		for(int i = 0; i < N; i++)
		{
			Par par = maxPar();
			int v = getIndex(par.par.charAt(0));
			int s = getIndex(par.par.charAt(1));
			frekvencaParov[v][s] = -1;
			lstPari.add(par);
		}
		
		//Obnovi pare!
		for(Par p : lstPari)
			obnoviPar(p);

		return lstPari;
	}
	
	private void obnoviPar(Par par)
	{
		int v = getIndex(par.par.charAt(0));
		int s = getIndex(par.par.charAt(1));
		frekvencaParov[v][s] = par.frekvenca;
	}
	
	
	public Par maxPar()
	{
		Par maxPar = new Par();
		//1. poišči maksimum
		for(int v = 0; v < MAX_PAROV; v++)
		{
			for(int s = 0; s < MAX_PAROV; s++)
			{
				//v,s tvori en par!
				if(maxPar.frekvenca < frekvencaParov[v][s])
				{
					maxPar.frekvenca = frekvencaParov[v][s];
					maxPar.par = String.format("%c%c", getCharacter(v), getCharacter(s));
				}
			}
		}
		return maxPar;
	}

	
	
	public boolean testirajMax(double max)
	{
		for(int v = 0; v < MAX_PAROV; v++)
		{
			for(int s = 0; s < MAX_PAROV; s++)
			{
				if(frekvencaParov[v][s] > max)
					return false;
			}
		}
		return true;
	}

	double razlika[][] = new double[MAX_PAROV][MAX_PAROV];
	
	public double razdalja(MatrikaParov mat, eTipRazdalje tip)
	{
		/*
		 * Računamo razdaljo med to matriko in
		 *  
		 */
		for(int i = 1; i < MAX_PAROV; i++)
		{
			for(int j = 1; j < MAX_PAROV; j++)
			{
				razlika[i][j] = frekvencaParov[i][j] - mat.frekvencaParov[i][j];
			}
		}
		switch(tip)
		{
		case evklidska:
			return evklidskaRazdalja(razlika);
		case manhattan:
			return manhattanRazdalja(razlika);
		case maximum:
			return maximumRazdalja(razlika);
		default:
			return evklidskaRazdalja(razlika);
		}
	}
	
	public static double evklidskaRazdalja(double[][] razlika) 
	{
		double suma = 0;
		for(int i = 0; i < MAX_PAROV; i++)
		{
			for(int j = 0; j < MAX_PAROV; j++)
			{
				suma = suma + razlika[i][j] * razlika[i][j];
			}
		}
		return Math.sqrt(suma);
	}
	
	public static double manhattanRazdalja(double[][] razlika) 
	{
		double suma = 0;
		for(int i = 0; i < MAX_PAROV; i++)
		{
			for(int j = 0; j < MAX_PAROV; j++)
			{
				suma = suma + Math.abs(razlika [i][j]);
			}
		}
		return (suma);
	}
	
	public static double maximumRazdalja(double[][] razlika) 
	{
		double maximum = 0;
		for(int i = 0; i < MAX_PAROV; i++)
		{
			for(int j = 0; j < MAX_PAROV; j++)
			{
				if(razlika[i][j] > maximum)
					maximum = razlika[i][j];
			}
		} 
		return maximum;
	}

	public static void main(String[] args) throws FileNotFoundException
	{
		StringBuffer testFile = Buffer.file2Buffer(new File("Test01.txt"));
		Substitucija sub = new Substitucija();
		List<String> file2list = Tokenizer.bufferToList(testFile);
		int st = 0;
		for(String s : file2list)
		{
			st++;
			System.out.println("Token " + st + ": " + s);
		}
		MatrikaParov matParov = new MatrikaParov();
		matParov.posodobiFrekvence(file2list);
		matParov.izpisiMatriko();
	}
	
	public MatrikaParov clone()
	{
		MatrikaParov mp = new MatrikaParov();
		for(int i = 0; i < MAX_PAROV; i++)
			for(int j = 0; j < MAX_PAROV; j++)
			{
				mp.frekvencaParov[i][j] = frekvencaParov[i][j];
			}
		mp.normaliziraj = normaliziraj;
		mp.sestevek = sestevek;
		return mp;
	}
}


